from itertools import groupby

from odoo import models, fields, api, _
from odoo.exceptions import UserError
from odoo.tools import float_compare


class LotDatecodeSummary(models.TransientModel):
    _name = 's.lot.datecode.summary.wiz'
    _description = 'Lot Datecode Summary'

    # datecode = fields.Char(string="Date Code")
    # ttl_qty = fields.Float(string="TTL Qty")
    # lot_ids = fields.Many2many('stock.lot', string='Domain Lot')
    # qty = fields.Float(string="Qty")
    # quant_ids = fields.Many2many('stock.quant', string='Allocated Quants')
    package_id = fields.Many2one('sb.picking.list')
    product_id = fields.Many2one('product.product', 'Product' )
    delivery_qty = fields.Float(string='Total Picks' )

    line_ids = fields.One2many('s.lot.datecode.summary.line.wiz', 'order_id', 'lines')

    def default_get(self, fields_list):
        result = super().default_get(fields_list)
        package = self._context.get('default_package_id')
        package_id = self.env['sb.picking.list'].browse(package)
        product_id = package_id.product_id
        product_ids = product_id.s_base_id.product_tmpl_ids.product_variant_ids
        product_ids |= product_id
        location_id = self.env['stock.location'].search([('barcode', '=', 'WH-STOCK-FINISHED-PRODUCT')], limit=1)
        location_ids = location_id.child_ids + location_id
        quant_ids = self.env['stock.quant'].search([
            ('product_id', 'in', product_ids.ids),
            ('location_id', 'in', location_ids.ids),
            ('quantity', '>', 0)
        ])

        line_vals_ids = []
        for q in quant_ids:
            line_quant = package_id.picking_list_lines.filtered(lambda x: x.quant_id == q)
            occupation_picking_list_lines = self.env['sb.picking.list.line'].search([
                ('quant_id', '=', q.id),
                ('picking_list_id.pd_id.state', '=', 'pack'),
                ('picking_list_id', '!=', package_id.id)
            ])
            free_qty = q.quantity - sum(occupation_picking_list_lines.mapped('quantity'))
            if float_compare(free_qty, 0, precision_rounding=q.product_id.uom_id.rounding) != 1:
                continue
            line_vals_ids.append((0, 0, {
                'quant_id': q.id,
                'product_id': q.product_id.id,
                'version': q.product_id.versions,
                'lot_id': q.lot_id.id,
                'free_qty': free_qty,
                'pick_qty': sum(line_quant.mapped('quantity'))
            }))
        result.update({
            'product_id': package_id.product_id.id,
            'delivery_qty': package_id.ttl_qty,
            'line_ids': line_vals_ids
        })

        return result

    def action_confirm(self):
        precision_rounding=self.product_id.uom_id.rounding
        line_ids = self.line_ids.filtered(lambda x: float_compare(x.pick_qty, 0, precision_rounding=precision_rounding) == 1)
        picking_list_line_vals_list = []
        for line in line_ids:
            if float_compare(line.free_qty, line.pick_qty, precision_rounding=precision_rounding) == -1:
                raise UserError(_("For datecode '%s', the entered qty (%.2f) exceeds the available quantity (%.2f)." % (line.datecode, line.pick_qty, line.free_qty)))
            picking_list_line_vals_list.append({
                'quant_id': line.quant_id.id,
                'product_id': line.quant_id.product_id.id,
                'picking_list_id': self.package_id.id,
                'pd_id': self.package_id.pd_id.id,
                'quantity': line.pick_qty
            })

        picking_list_datecode_line_vals_list = []
        all_datecodes = []
        for (version, datecode), g in groupby(line_ids.sorted(lambda x: (x.version, x.datecode)), key=lambda x: (x.version, x.datecode)):
            lines = self.env['s.lot.datecode.summary.line.wiz'].concat(*g)
            all_datecodes.append(datecode)
            picking_list_datecode_line_vals_list.append({
                'package_id': self.package_id.id,
                'product_id': lines[0].product_id.id,
                'version': version,
                'datecode': datecode,
                'qty': sum(lines.mapped('pick_qty'))
            })
        all_datecodes = list(set(all_datecodes))
        if all_datecodes:
            self.package_id.note = ", ".join(all_datecodes)
        else:
            self.package_id.note = _('No Date Code')
        self.package_id.picking_list_lines.sudo().unlink()
        self.env['sb.picking.list.line'].sudo().create(picking_list_line_vals_list)
        self.package_id.datecode_line_ids.sudo().unlink()
        self.env['sb.picking.list.datecode.line'].sudo().create(picking_list_datecode_line_vals_list)

    def action_update_package_multi(self):
        """
        This method performs the following operations on a set of wizard records:
        1. Ensure all selected records belong to the same package.
        2. Validate that each record's qty does not exceed its ttl_qty.
        3. For each record, sort the associated lots by 's_week_of_year' and collect lot IDs.
        4. Combine the lot IDs from all records (remove duplicates).
        5. Update the package record:
           - Set lot_ids with the combined lot IDs.
           - Set the note field with the concatenated datecode values.
           - Set quant_ids with the stock quant records corresponding to the combined lot IDs.
           - Set a one2many field datecode_line_ids with datecode:qty.
           - Create picking_list_line records to record quant usage.
        6. Return an action to close the wizard window.

        ！！！似乎调拨了也没什么意义   根据date code数量限制之后再从生成生成出货单的时候生成调拨就好了
        """
        if not self:
            return

        # Filter out lines with qty == 0
        lines = self.filtered(lambda r: r.qty > 0)

        # Ensure all records have a related package
        if any(not rec.package_id for rec in lines):
            raise UserError(_("All records must be linked to a package."))
        package = lines[0].package_id
        for rec in lines:
            if rec.package_id != package:
                raise UserError(_("Selected records belong to different packages."))
        ttl_qty = 0
        for rec in lines:
            ttl_qty += rec.qty
            if rec.qty > rec.ttl_qty:
                raise UserError(_("For datecode '%s', the entered qty (%.2f) exceeds the available quantity (%.2f)." % (rec.datecode, rec.qty, rec.ttl_qty)))
        p_ttl = package.ttl_qty + package.free_sample
        if ttl_qty != p_ttl:
            raise UserError(_("The sum of the entered qty (%.2f) does not match the total quantity (%.2f)." % (ttl_qty, package.p_ttl)))

        all_lot_ids = []
        all_datecodes = []
        for rec in lines:
            sorted_lots = rec.lot_ids.sorted(key=lambda lot: lot.s_week_of_year or '')
            all_lot_ids += sorted_lots.ids
            all_datecodes.append(rec.datecode)
        all_lot_ids = list(dict.fromkeys(all_lot_ids))

        package.lot_ids = [(6, 0, all_lot_ids)]
        package.note = ", ".join(all_datecodes)

        Quant = self.env['stock.quant']
        stock_locations = self.env['stock.location'].search([('name', '=', '成品仓')])
        if not stock_locations:
            raise UserError(_("未找到名为“成品仓”的库位"))

        # 限定只查成品仓的 quant
        quants = Quant.search([
            ('lot_id', 'in', all_lot_ids),
            ('available_quantity', '>', 0),
            ('location_id', 'in', stock_locations.ids),
        ])
        package.quant_ids = [(6, 0, quants.ids)]
        package.quant_ids = [(6, 0, quants.ids)]

        package.datecode_line_ids.unlink()
        datecode_line_vals = []
        for rec in lines:
            datecode_line_vals.append((0, 0, {
                'datecode': rec.datecode,
                'qty': rec.qty,
            }))
        package.datecode_line_ids = datecode_line_vals

        PickingListLine = self.env['sb.picking.list.line']
        existing_lines = PickingListLine.search([('picking_list_id', '=', package.id)])
        existing_lines.unlink()
        for rec in lines:
            remaining_qty = rec.qty
            sorted_quants = Quant.search([
                ('lot_id', 'in', rec.lot_ids.ids),
                ('available_quantity', '>', 0),
                ('location_id', 'in', stock_locations.ids),
            ], order='in_date asc')

            for quant in sorted_quants:
                if remaining_qty <= 0:
                    break
                available_qty = quant.available_quantity
                alloc_qty = min(available_qty, remaining_qty)
                if alloc_qty <= 0:
                    continue
                PickingListLine.create({
                    'picking_list_id': package.id,
                    'quant_id': quant.id,
                    'quantity': alloc_qty,
                    'pd_id': package.pd_id.id,
                })
                remaining_qty -= alloc_qty

            if remaining_qty > 0:
                raise UserError(_("Not enough stock to fulfill datecode '%s' (%.2f short)." % (rec.datecode, remaining_qty)))

        return {'type': 'ir.actions.act_window_close'}


class LotDatecodeSummaryLine(models.TransientModel):
    _name = 's.lot.datecode.summary.line.wiz'
    _description = 'Lot Datecode Summary Line'
    _order = 'datecode'

    order_id = fields.Many2one('s.lot.datecode.summary.wiz')
    quant_id = fields.Many2one('stock.quant', 'Quant')
    product_id = fields.Many2one('product.product', 'Product')
    lot_id = fields.Many2one('stock.lot')
    version = fields.Char('version')
    datecode = fields.Char('Date Code', related='lot_id.s_week_of_year')
    free_qty = fields.Float('free quantity')
    pick_qty = fields.Float('Pick quantity')
    package_id = fields.Many2one('sb.picking.list')